from __future__ import generators
import sys
sys.path.append("/var/lib/zope/Extensions")
import random
import inspect
import posnet_compare

#--------------------------------------
request_str2 = lambda req:  " < ".join([  ",".join( [str(k)+"="+str(v) for k,v in elem.items()]) for elem in req])
request_str  = lambda req:  ",".join( [str(k)+"="+str(v) for k,v in req.items()])
#--------------------------------------

#def get_algorithm(name):
#	global algorithms
#	if not name in algorithms.keys(): return ""
#	func= str( algorithms[name].__name__ )+"_handler"
#	func = globals()[str(func)]	
#	code = inspect.getsource(func)
#	return code.replace("\n","<br>").replace(" ","\t")
#

def all_match_range_query_handler(request_value,node,data,sender,link_type):
    r = data["space_point"]
    is_top = False
    if len(data["parents"]) == 0: is_top = True
    nb_peer = len(data["peers"])
    if link_type == "" or link_type == "c2p": # noeud d'entree ou en train de monter
        if is_top: # on arrive sur un noeud au top
            # on transmet a tous les voisins
            for peer in data["peers"]:
                yield (request_value,peer)
            # ++ transmission eventuelle aux clients de ce top
            #     est-ce ce top fit la requete?
            compare_value_up =   posnet_compare.compare(r,request_value[1])
            compare_value_down =   posnet_compare.compare(r,request_value[0])
            if compare_value_up in (0,1) and compare_value_down in (0,2): yield node  # match, donc on retourne le noeud !
            if compare_value_down == 2: # ce noeud inclus la bas de la requete range, donc des responses sont possibles ! 
            # ce noeud n'est pas inclus, on descends	
                for child in data["childs"]:
                    yield (request_value,child)
        else: # on est pas encore au top => on remonte encore la requete, mais a un seul parent (ca suffit un seul)
            premier_parent = random.choice(data["parents"])
            yield (request_value,premier_parent)
    else: # on descend ou alors transmission d'un top vers un autre
        compare_value_up =   posnet_compare.compare(r,request_value[1])  
        compare_value_down =   posnet_compare.compare(r,request_value[0])
        if compare_value_up in (0,1) and compare_value_down in (0,2): yield node  # match, donc on retourne le noeud !
        if compare_value_down == 2: # ce noeud inclus la bas de la requete range, donc des responses sont possibles ! 
        # ce noeud n'est pas inclus, on descends	
            for child in data["childs"]:
                yield (request_value,child)


def all_top_point_query_handler(request_value,node,data,sender,link_type):
	r = data["space_point"]
	is_top = False
	if len(data["parents"]) > 0:
		premier_parent = random.choice(data["parents"])
		#print "in all_top(",node,"): propagate to random parent:",premier_parent
		yield (request_value,premier_parent)
	else:
		#print "in all_top(",node,"): i'm a top node :)"	
		yield node
		if link_type == "" or link_type == "c2p": # noeud d'entree ou en train de monter
			for peer in data["peers"]:
				#print "in all_top(",node,"): propagate to",peer,"(link=",link_type,")"
				yield (request_value,peer)
		#else: print "in all_top(",node,"): do not propagate"

def parents_point_query_handler(request_value,node,data,sender,link_type):
	# parents query should be received by the node
	for parent in data["parents"]:
		#print "PARENTS",data["parents" ]
		yield parent

def exact_point_query_handler(request_value,node,data,sender,link_type):
	#print >>sys.stdout,"exact: request=",request_value,", node=",node,", sender=",sender,", link=",link_type,"data=",data
	#sys.stdout.flush()
	if link_type == "" or link_type == "c2p": # ce noeud est point d'entree
		is_top = False
		if len(data["parents"]) == 0: is_top = True
		r = data["space_point"]
		found = False
		compare_value =   posnet_compare.compare(r,request_value)
		if compare_value == 0: # exact match
			yield node
			found = True
		elif compare_value == 2:
			# should go down
			found = False
			child_to_give  = []
			for child in data["childs"]:
				# si le child contient on lui transmet
				rchild = data["neighbors"][child]
				compare_value =   posnet_compare.compare(rchild,request_value)
				if compare_value == 0: # exact match
					yield child
					found = True
					break
				elif  compare_value == 2:
					child_to_give.append(child)
			if not found and len(child_to_give) > 0:
				child = random.choice(child_to_give)
				yield (request_value,child)
				found = True
		if not found:
			if is_top: # on arrive sur un noeud au top, donc la on redecends par un pere du resultat 
				peer_to_give  = []
				for peer in data["peers"]:
					# si le peer contient on lui transmet
					rpeer = data["neighbors"][peer]
					compare_value =   posnet_compare.compare(rpeer,request_value)
					if compare_value == 0: # exact match
						yield peer
						found = True
						break
					elif  compare_value == 2:
						peer_to_give.append(peer)
				if not found and len(peer_to_give) > 0:
					peer = random.choice(peer_to_give)
					yield (request_value,peer)
			else:
				interesting_parents  = []
				for parent in data["parents"]:
					# si le peer contient on lui transmet
					rparent = data["neighbors"][parent]
					compare_value =   posnet_compare.compare(rparent,request_value)
					if compare_value == 0: # exact match
						yield parent
						found = True
						break
					elif  compare_value == 2:
						interesting_parents.append(parent)
				if not found:
					if len(interesting_parents) > 0:
						parent = random.choice(interesting_parents)
						yield (request_value,parent)
					else:
						parent = random.choice(data["parents"])
						yield (request_value,parent)
	else: # on descends ou on emprunte un lien p2p, cela veut dire que l'on contenait la valeur, on cherche un fils qui va...
		fit = []
		found = False
		for child in data["childs"]:
			#r = data["space_point"]
			rchild = data["neighbors"][child]
			compare_value =   posnet_compare.compare(rchild,request_value)
			if compare_value == 0: # on a trouve le resultat
				yield child
				found = True
				break
			elif compare_value == 2:
				fit.append(child)
		if not found:
			if len(fit) > 0: # on propage vers un child qui contient...
				child = random.choice(fit)
				yield (request_value,child)


def all_children_point_query_handler(request_value,node,data,sender,link_type):
    r = data["space_point"]
    is_top = False
    if len(data["parents"]) == 0: is_top = True
    if link_type == "" or link_type == "c2p": # noeud d'entree ou en train de monter
        if is_top: # on arrive sur un noeud au top
            # on transmet a tous les voisins
            for peer in data["peers"]:
                yield (request_value,peer)
            # ++ transmission eventuelle aux clients de ce top
            #     est-ce ce top fit la requete?
            compare_value =   posnet_compare.compare(r,request_value)
            if compare_value == 0: yield node  # match exact, donc on retourne le noeud !
            elif compare_value == 1: # ce noeud est inclus et il est top, donc on renvoi !
               yield node
            else:
            # ce noeud n'est pas inclus, on descends	
                for child in data["childs"]:
                    yield (request_value,child)
        else: # on est pas encore au top => on remonte encore la requete, mais a un seul parent (ca suffit un seul)
            premier_parent = random.choice(data["parents"])
            yield (request_value,premier_parent)
    else: # on descend ou alors transmission d'un top vers un autre
        compare_value =   posnet_compare.compare(r,request_value)
        if compare_value == 0: yield node
        elif compare_value == 1: # ce noeud est inclus dans le result, on le renvoi et pas de propag
            yield node
        else:
            for child in data["childs"]:
                yield (request_value,child)
    
def all_parents_point_query_handler(request_value,node,data,sender,link_type):
    r = data["space_point"]
    is_top = False
    if len(data["parents"]) == 0: is_top = True
    nb_peer = len(data["peers"])
    if link_type == "" or link_type == "c2p": # noeud d'entree ou en train de monter
        if is_top: # on arrive sur un noeud au top
            # on transmet a tous les voisins au top qui fit :)
            for peer in data["peers"]:
                peer_resource =  data["neighbors"][peer]
                compare_value_for_this_peer =   posnet_compare.compare(peer_resource,request_value)
                if compare_value_for_this_peer == 0 or compare_value_for_this_peer == 2:
                    # on transmet ce top car il fit
                    yield (request_value,peer)
            # ++ transmission eventuelle aux clients de ce top
            #     est-ce ce top fit la requete?
            compare_value =   posnet_compare.compare(r,request_value)
            if compare_value == 0: yield node  # match exact, donc on retourne le noeud !
            elif compare_value == 2: # ce noeud fit, voyons si un child fit ?
                # on transmet aux clients de ce top qui fit ou alors ce noeud est le seul fitter et on le retourne
                nb_fitting_childs = 0
                for child in data["childs"]:
                    child_resource = data["neighbors"][child]
                    compare_value_for_child =   posnet_compare.compare(child_resource,request_value)
                    if compare_value_for_child == 0 or compare_value_for_child == 2:
                        # on propage vers ce fils car il fit
                        yield (request_value,child)
                        nb_fitting_childs += 1
                if nb_fitting_childs == 0:
                    # aucun fils ne fit, donc on retourne ce noeud et aucune propagation
                    yield node
        else: # on est pas encore au top => on remonte encore la requete, mais a un seul parent (ca suffit un seul)
            premier_parent =   random.choice(data["parents"])
            yield (request_value,premier_parent)
    else: # on descend ou alors transmission d'un top vers un autre
        # quoiqu'il en soit, on sait deja que ce noeud fit sinon la requete ne serait pas arrivee jusqu'ici
        # on examine maintenant les childs
        nb_fitting_childs = 0
        for child in data["childs"]:
            child_resource = data["neighbors"][child]
            compare_value_for_child =   posnet_compare.compare(child_resource,request_value)
            if compare_value_for_child == 0 or compare_value_for_child == 2:
            # on propage vers ce fils car il fit
                yield (request_value,child)
                nb_fitting_childs += 1
        if nb_fitting_childs == 0:
        # aucun fils ne fit, donc on retourne ce noeud et aucune propagation
            yield node

    
def all_fit_point_query_handler(request_value,node,data,sender,link_type):
	r = data["space_point"]
	nb = len(data["peers"]) + len(data["parents"])
	if nb > 0 and link_type in ("c2p",""): 	
		if len(data["parents"]) >  0:
			yield (request_value,data["parents"][0])
			#for parent in data["parents"]: yield (request_value,parent)
		else:
			for peer in data["peers"]: yield (request_value,peer)
	else:
		compare_value =   posnet_compare.compare(r,request_value)
		if compare_value == 0: yield node  # match
		elif compare_value == 2:
			yield node # fit
			for child in data["childs"]: yield (request_value,child)


def first_fit_point_query_handler(request_value,node,data,sender,link_type):
	r = data["space_point"]
	compare_value =   posnet_compare.compare(r,request_value)
	if compare_value == 0: yield node  # exact match
	elif compare_value == 1:
		if link_type in ("c2p",""): # on est en train de monter ou on rentre sur l'overlay
			for parent in data["parents"]:
				yield (request_value,parent)
	elif compare_value == 2:
		yield node # fit
		#for child in data["childs"]:
		#	if child != sender: yield (request,child,node,"p2c")
	elif compare_value == -1:
		if link_type in ("","c2p"):
			for parent in data["parents"]: yield (request_value,parent)
			for peer in data["peers"]: yield (request_value,peer)


def first_fit2_point_query_handler(request_value,node,data,sender,link_type):
	def does_it_fit(rval):
		cval = posnet_compare.compare(rval,request_value)
		if cval in (0,2): return True
		else: return False
	found = False
	if link_type == "":
		if does_it_fit(data["space_point"]):
			found = True
			yield node
	if not found and link_type in ("c2p",""):
		for parent in data["parents"]:
                        #print "check parent",data["neighbors"][parent]
			if does_it_fit(data["neighbors"][parent]):
				found = True
				yield parent
				break 
		for peer in data["peers"]:
                        #print "check peer",data["neighbors"][peer]
			if does_it_fit(data["neighbors"][peer]):
				found = True
				yield peer
				break
		if not found:
                        #print "was not found => transmit"
			# was not found !
			for parent in data["parents"]: yield (request_value,parent)
			for peer in data["peers"]: yield (request_value,peer)
        sys.stdout.flush()

